O time de aprendizagem da Universidade quer entender qual o perfil das pessoas estudantes, quais são os fatores relacionado ao desempenho das pessoas e quais recomendações/iniciativas você sugere para que o desempenho das pessoas estudantes melhore.
O trabalho foi explorar os dados dispobilizados e direcionar a tomada de decisão do negócio.
Um conjunto de dados contendo informações demográficas sobre os alunos, seus cursos frequentados e os resultados finais de cada curso.
O conjunto de dados anônimos da Open University Learning Analytics Dataset (OULAD), contém dados sobre cursos, alunos e suas interações com o Virtual Learning Environment (VLE) para sete cursos selecionados. As apresentações dos cursos começão em fevereiro e outubro - são marcadas como “B” e “J” respectivamente. O conjunto de dados consiste em tabelas conectadas usando identificadores exclusivos. Todas as tabelas são armazenadas no formato csv.
Kuzilek J., Hlosta M., Zdrahal Z. Open University Learning Analytics dataset Sci.
Aqui temos um esquema (https://analyse.kmi.open.ac.uk/open_dataset) para ilustrar a estrutura de dados do conjunto.
Como você pode ver, existem muitos tipos diferentes de dados envolvidos, mas como queremos entender o perfil das pessoas estudantes e os fatores de desempenho utilizaremos:
Indo para o site indicado, podemos ver que estas informações estam contidas nas seguintes tabelas:
Essas tabelas serão nossas fontes de dados para cumprimento dos objetivos.
Para realizar essa análise contaremos com o uso de dois pacotes do R, o dplyr e o plotly. Um para auxiliar na manipulação das tabelas e o outro na geração dos gráficos que serão apresentados.
Os pacotes estão disponíveis em:
Ou utilizando os comandos:
# install.packages("dplyr")
# install.packages("plotly")
Vamos utilizar o libary para chamar os pacotes após a instalação:
library(dplyr)
library(plotly)
Para finalizarmos a preparação do ambiente devemos informar o local onde se encontra os dados de entrada (inputs):
# setwd(dir = ".../Input")
Para podermos entender o perfil dos alunos presentes na base de dados utilizaremos a tabela studentInfo pois a mesma contém informações demográficas sobre os alunos junto com seus resultados. O arquivo contém as seguintes colunas:
student_info <-
read.csv(
file = paste0(getwd(),"/Input/studentInfo.csv")
)
colnames(student_info)
## [1] "code_module" "code_presentation" "id_student"
## [4] "gender" "region" "highest_education"
## [7] "imd_band" "age_band" "num_of_prev_attempts"
## [10] "studied_credits" "disability" "final_result"
Utilizaremos apenas as colunas que contém informações ligadas ao perfil demográfico do aluno, deixaremos também apenas os valores sem repetição, para termos um dado único de cada aluno:
student_info_profile <-
student_info[,c(2:6,8,11)] %>%
distinct(id_student, .keep_all = TRUE)
colnames(student_info_profile)
## [1] "code_presentation" "id_student" "gender"
## [4] "region" "highest_education" "age_band"
## [7] "disability"
Gerando os quantitativos:
#Tipos de apresentação e a quantidade de alunos
student_info_presentation <-
student_info_profile %>%
mutate(
type_presentation = substr(code_presentation, nchar(code_presentation), nchar(code_presentation))
) %>%
group_by(type_presentation) %>%
count() %>%
ungroup()
student_info_presentation
## # A tibble: 2 x 2
## type_presentation n
## <chr> <int>
## 1 B 11190
## 2 J 17595
#Alunos por gênero
student_info_gender <-
student_info_profile %>%
group_by(gender) %>%
count() %>%
ungroup()
student_info_gender
## # A tibble: 2 x 2
## gender n
## <chr> <int>
## 1 F 13739
## 2 M 15046
#Alunos por regiâo
student_info_region <-
student_info_profile %>%
group_by(region) %>%
count() %>%
ungroup()
student_info_region
## # A tibble: 13 x 2
## region n
## <chr> <int>
## 1 East Anglian Region 3000
## 2 East Midlands Region 2095
## 3 Ireland 1072
## 4 London Region 2845
## 5 North Region 1588
## 6 North Western Region 2548
## 7 Scotland 2934
## 8 South East Region 1875
## 9 South Region 2737
## 10 South West Region 2154
## 11 Wales 1876
## 12 West Midlands Region 2269
## 13 Yorkshire Region 1792
#Escolaridade
student_info_education <-
student_info_profile %>%
group_by(highest_education) %>%
count() %>%
ungroup()
student_info_education
## # A tibble: 5 x 2
## highest_education n
## <chr> <int>
## 1 A Level or Equivalent 12355
## 2 HE Qualification 4092
## 3 Lower Than A Level 11780
## 4 No Formal quals 306
## 5 Post Graduate Qualification 252
#Idade
student_info_age <-
student_info_profile %>%
group_by(age_band) %>%
count() %>%
ungroup()
student_info_age
## # A tibble: 3 x 2
## age_band n
## <chr> <int>
## 1 0-35 20145
## 2 35-55 8462
## 3 55<= 178
#Portador de deficiência
student_info_disability <-
student_info_profile %>%
group_by(disability) %>%
count() %>%
ungroup()
student_info_disability
## # A tibble: 2 x 2
## disability n
## <chr> <int>
## 1 N 26068
## 2 Y 2717
#Informações demográficas compiladas
student_demographic_data <-
student_info_profile %>%
group_by(gender, region, highest_education, age_band, disability) %>%
count() %>%
ungroup()
student_demographic_data
## # A tibble: 336 x 6
## gender region highest_education age_band disability n
## <chr> <chr> <chr> <chr> <chr> <int>
## 1 F East Anglian Region A Level or Equivalent 0-35 N 392
## 2 F East Anglian Region A Level or Equivalent 0-35 Y 70
## 3 F East Anglian Region A Level or Equivalent 35-55 N 169
## 4 F East Anglian Region A Level or Equivalent 35-55 Y 29
## 5 F East Anglian Region HE Qualification 0-35 N 65
## 6 F East Anglian Region HE Qualification 35-55 N 66
## 7 F East Anglian Region Lower Than A Level 0-35 N 399
## 8 F East Anglian Region Lower Than A Level 0-35 Y 60
## 9 F East Anglian Region Lower Than A Level 35-55 N 200
## 10 F East Anglian Region Lower Than A Level 35-55 Y 41
## # ... with 326 more rows
Observando os gráficos chegamos a conclusão que temos um perfil multicultural de alunos, vindos de diversas regiões, com diversos níveis de conhecimento diferentes, com a maioria tendo até 35 anos de idade. Porém decidimos correlacioanar os dados que julgamos mais importantes(idade, escolaridade e região) para entender melhor o perfil da amostra:
#Escolaridade por região dos alunos
student_info_region_education <-
student_info_profile %>%
group_by(region, highest_education) %>%
count() %>%
ungroup()
student_info_region_education
## # A tibble: 60 x 3
## region highest_education n
## <chr> <chr> <int>
## 1 East Anglian Region A Level or Equivalent 1305
## 2 East Anglian Region HE Qualification 313
## 3 East Anglian Region Lower Than A Level 1324
## 4 East Anglian Region No Formal quals 49
## 5 East Anglian Region Post Graduate Qualification 9
## 6 East Midlands Region A Level or Equivalent 944
## 7 East Midlands Region HE Qualification 176
## 8 East Midlands Region Lower Than A Level 960
## 9 East Midlands Region No Formal quals 11
## 10 East Midlands Region Post Graduate Qualification 4
## # ... with 50 more rows
Qundo correlacionamos a escolaridade por região, vemos uma amsotra ainda homogênea.
#Escolaridade por idade dos alunos
student_info_age_education <-
student_info_profile %>%
group_by(age_band, highest_education) %>%
count() %>%
ungroup()
student_info_age_education
## # A tibble: 14 x 3
## age_band highest_education n
## <chr> <chr> <int>
## 1 0-35 A Level or Equivalent 9290
## 2 0-35 HE Qualification 2228
## 3 0-35 Lower Than A Level 8284
## 4 0-35 No Formal quals 258
## 5 0-35 Post Graduate Qualification 85
## 6 35-55 A Level or Equivalent 3032
## 7 35-55 HE Qualification 1756
## 8 35-55 Lower Than A Level 3469
## 9 35-55 No Formal quals 48
## 10 35-55 Post Graduate Qualification 157
## 11 55<= A Level or Equivalent 33
## 12 55<= HE Qualification 108
## 13 55<= Lower Than A Level 27
## 14 55<= Post Graduate Qualification 10
Agora, correlacionando Idade x Escolaridade, conseguimos indentificar pontos onde existe uma maior quantidade de amostras, assim decidimos criar uma tabela apenas dos estudantes que contém esse perfil:
#Escolaridade por idade dos alunos
representative_student_group_info <-
student_info_profile %>%
subset(
age_band == "0-35" &
(
highest_education == "A Level or Equivalent" |
highest_education == "Lower Than A Level" |
highest_education == "HE Qualification"
)
)
#O quanto esse grupo representa
group_percentage <-
(nrow(representative_student_group_info)/nrow(student_info_profile))*100
group_percentage
## [1] 68.79277
Após essas correlações, observar-se que as pessoas estudantes de idade ‘0-35’ que possuem Nível superior, ou Ensino médio completo/cursando, representam o perfil geral dos alunos, por serem 68.8% das amostras..
O desempenho em cada avaliação é um bom indicador do conhecimento dos alunos sobre o curso. Iremos separar os exames finais das restantes avaliações, dado que o seu estatuto e a participação na avaliação final são diferentes das restantes.
#Informações das provas por estudante
student_assessment <-
read.csv(
file = paste0(getwd(),"/Input/studentAssessment.csv")
)
#Informações das provas
assessments <-
read.csv(
file = paste0(getwd(),"/Input/assessments.csv")
)
final_exams <-
assessments %>%
subset(assessment_type == "Exam")
head(final_exams)
## code_module code_presentation id_assessment assessment_type date weight
## 6 AAA 2013J 1757 Exam NA 100
## 12 AAA 2014J 1763 Exam NA 100
## 24 BBB 2013B 14990 Exam NA 100
## 36 BBB 2013J 15002 Exam NA 100
## 48 BBB 2014B 15014 Exam NA 100
## 54 BBB 2014J 15025 Exam NA 100
others_exams <-
assessments %>%
subset(assessment_type != "Exam")
head(others_exams)
## code_module code_presentation id_assessment assessment_type date weight
## 1 AAA 2013J 1752 TMA 19 10
## 2 AAA 2013J 1753 TMA 54 20
## 3 AAA 2013J 1754 TMA 117 20
## 4 AAA 2013J 1755 TMA 166 20
## 5 AAA 2013J 1756 TMA 215 30
## 7 AAA 2014J 1758 TMA 19 10
Vamos indentificar qual a média de avaliação por aluno por modulo, e indentificar as atividade de quem tem as maiores e menores médias de avaliação.
#Criando data frame 'student_group_kpis'
student_group_kpis <-
student_assessment %>%
mutate(pass = ifelse(score>=40, TRUE, FALSE))
#Juntando com as informações de exame e criando as colunas de quem passou no exame e o peso da grade
student_group_others_exams <-
student_group_kpis %>%
inner_join(others_exams, by = "id_assessment")
student_group_others_exams <-
student_group_others_exams %>%
mutate(weight_grade = score*weight/100)
head(student_group_others_exams[,c(1,6,7,11)])
## id_assessment pass code_module weight
## 1 1752 TRUE AAA 10
## 2 1752 TRUE AAA 10
## 3 1752 TRUE AAA 10
## 4 1752 TRUE AAA 10
## 5 1752 TRUE AAA 10
## 6 1752 TRUE AAA 10
#Média de avaliação final por aluno por módulo
avg_grade_others_exams <-
student_group_others_exams %>%
dplyr::group_by(id_student, code_module, code_presentation) %>%
mutate(avg_grade = sum(weight_grade)) %>%
select("id_student","code_module","code_presentation", "avg_grade")
head(avg_grade_others_exams)
## # A tibble: 6 x 4
## # Groups: id_student, code_module, code_presentation [6]
## id_student code_module code_presentation avg_grade
## <int> <chr> <chr> <dbl>
## 1 11391 AAA 2013J 82.4
## 2 28400 AAA 2013J 65.4
## 3 31604 AAA 2013J 76.3
## 4 32885 AAA 2013J 55
## 5 38053 AAA 2013J 66.9
## 6 45462 AAA 2013J 67.8
#Pontuação dos exames finais
student_group_final_exams <-
student_group_kpis %>%
inner_join(final_exams, by = "id_assessment") %>%
dplyr::rename("exams_score" = "score")%>%
select("id_student","code_module","code_presentation", "exams_score")
head(student_group_final_exams)
## id_student code_module code_presentation exams_score
## 1 558914 CCC 2014B 32
## 2 559706 CCC 2014B 78
## 3 559770 CCC 2014B 54
## 4 560114 CCC 2014B 64
## 5 560311 CCC 2014B 100
## 6 560494 CCC 2014B 92
Tendo levantado os dados das avaliações, vamos verificar os dados de interações do aluno com o ambiente virtual da universidade
Os conjuntos de dados referentes ao ambiente virtual da universidade contêm o feed de interação dos alunos com o conteúdo disponível. A partir desses dados, podemos inferir como um aluno estava em contato com seus assuntos, se o estudou de forma sólida e como utilizou o conteúdo.
#Lendo as tabelas de interações
student_vle <-
read.csv(
file = paste0(getwd(),"/Input/studentVle.csv")
)
head(student_vle)
## code_module code_presentation id_student id_site date sum_click
## 1 AAA 2013J 28400 546652 -10 4
## 2 AAA 2013J 28400 546652 -10 1
## 3 AAA 2013J 28400 546652 -10 1
## 4 AAA 2013J 28400 546614 -10 11
## 5 AAA 2013J 28400 546714 -10 1
## 6 AAA 2013J 28400 546652 -10 8
vle <-
read.csv(
file = paste0(getwd(),"/Input/vle.csv")
)
head(vle)
## id_site code_module code_presentation activity_type week_from week_to
## 1 546943 AAA 2013J resource NA NA
## 2 546712 AAA 2013J oucontent NA NA
## 3 546998 AAA 2013J resource NA NA
## 4 546888 AAA 2013J url NA NA
## 5 547035 AAA 2013J resource NA NA
## 6 546614 AAA 2013J homepage NA NA
Se observarmos a tabela VLE, podemos indentificar que existem alguns dados sem referência de périodo de uso, portanto, para tornar a análise mais viável iremos filtra-los.
#limpando os dados de VLE, pois algumas amostras não possuem a semana de referência para os materiais
vle <-
vle %>%
subset(!is.na(week_from))
head(vle)
## id_site code_module code_presentation activity_type week_from week_to
## 114 546732 AAA 2013J oucontent 2 2
## 199 546719 AAA 2013J oucontent 1 1
## 211 546681 AAA 2013J oucontent 1 1
## 265 877040 AAA 2014J oucontent 2 2
## 324 877045 AAA 2014J oucontent 1 1
## 392 877044 AAA 2014J oucontent 1 1
Aqui podemos acompanhar o tempo médio após o início do curso que o aluno fez para utilizar os materiais e a quantidade média de cliques por material:
#Média geral por aluno por módulo
avg_per_student <-
student_vle %>%
dplyr::group_by(id_student, code_module, code_presentation) %>%
mutate(
date_mean = mean(date),
sum_click_mean = mean(sum_click)) %>%
select("id_student","code_module","code_presentation", "date_mean", "sum_click_mean")
head(avg_per_student)
## # A tibble: 6 x 5
## # Groups: id_student, code_module, code_presentation [1]
## id_student code_module code_presentation date_mean sum_click_mean
## <int> <chr> <chr> <dbl> <dbl>
## 1 28400 AAA 2013J 87.0 3.34
## 2 28400 AAA 2013J 87.0 3.34
## 3 28400 AAA 2013J 87.0 3.34
## 4 28400 AAA 2013J 87.0 3.34
## 5 28400 AAA 2013J 87.0 3.34
## 6 28400 AAA 2013J 87.0 3.34
Como não podemos indentificar fatores de desempenho nos estudantes que desitiram, iremos tirar eles das amostras representativas:
#Filtrando somente amostras representativas (Segundo a analise de perfil dos estudantes)
representative_student_group_info <-
student_info %>%
subset(
age_band == "0-35" &
(
highest_education == "A Level or Equivalent" |
highest_education == "Lower Than A Level" |
highest_education == "HE Qualification"
) &
final_result != "Withdrawn"
) %>%
distinct(id_student, .keep_all = TRUE)
#Compilando as tabelas relevantes
df_1 <-
inner_join(avg_grade_others_exams, student_group_final_exams,
by = c("id_student", "code_module", "code_presentation"))
df_2 <-
inner_join(representative_student_group_info, df_1,
by = c("id_student", "code_module", "code_presentation"))
final_df <-
inner_join(df_2, avg_per_student,
by = c("id_student", "code_module", "code_presentation")) %>%
select(num_of_prev_attempts, final_result, avg_grade, exams_score, date_mean, sum_click_mean)
head(final_df[,-2])
## num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 1 0 89.65 94 119.3379 4.343939
## 2 0 89.65 94 119.3379 4.343939
## 3 0 89.65 94 119.3379 4.343939
## 4 0 89.65 94 119.3379 4.343939
## 5 0 89.65 94 119.3379 4.343939
## 6 0 89.65 94 119.3379 4.343939
summary(final_df[,-2])
## num_of_prev_attempts avg_grade exams_score date_mean
## Min. :0.0000 Min. : 3.72 Min. : 0.00 Min. : 27.01
## 1st Qu.:0.0000 1st Qu.:58.88 1st Qu.: 51.00 1st Qu.: 89.75
## Median :0.0000 Median :74.25 Median : 67.00 Median :103.28
## Mean :0.1059 Mean :70.98 Mean : 65.98 Mean :103.55
## 3rd Qu.:0.0000 3rd Qu.:86.15 3rd Qu.: 82.00 3rd Qu.:115.64
## Max. :5.0000 Max. :99.80 Max. :100.00 Max. :230.00
## NA's :20653
## sum_click_mean
## Min. : 1.077
## 1st Qu.: 2.273
## Median : 2.681
## Mean : 2.956
## 3rd Qu.: 3.331
## Max. :16.242
##
nrow(final_df[final_df$final_result == "Pass",])
## [1] 8043978
nrow(final_df[final_df$final_result == "Distinction",])
## [1] 2145211
nrow(final_df[final_df$final_result == "Fail",])
## [1] 1282021
Com uma contagem de “Pass” muito maior do que os outros rótulos, devemos ficar atentos. Foram detectados dois outliers: Um com média de cliques bem acima dos valores padrões e outro com uma única ocorrência de um número de tentativas anteriores. Para manter nossos dados o mais consistentes possível, esses casos serão removidos.
final_df <-
final_df %>%
subset(sum_click_mean<10)
final_df <-
final_df %>%
subset(num_of_prev_attempts<4)
nrow(final_df)
## [1] 11435514
pass_student <-
final_df %>%
subset(final_result != "Fail")
head(pass_student[,-2])
## num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 1 0 89.65 94 119.3379 4.343939
## 2 0 89.65 94 119.3379 4.343939
## 3 0 89.65 94 119.3379 4.343939
## 4 0 89.65 94 119.3379 4.343939
## 5 0 89.65 94 119.3379 4.343939
## 6 0 89.65 94 119.3379 4.343939
nrow(pass_student)
## [1] 10155306
fail_student <-
final_df %>%
subset(final_result == "Fail")
head(fail_student[,-2])
## num_of_prev_attempts avg_grade exams_score date_mean sum_click_mean
## 42369 0 40.96 24 96.93333 5.114286
## 42370 0 40.96 24 96.93333 5.114286
## 42371 0 40.96 24 96.93333 5.114286
## 42372 0 40.96 24 96.93333 5.114286
## 42373 0 40.96 24 96.93333 5.114286
## 42374 0 40.96 24 96.93333 5.114286
nrow(fail_student)
## [1] 1280208
Como temos uma grande quantidade de amostras, utilizarei abaixo apenas as primeiras cem mil amostras (100.000) para montar os gráficos:
Após a análise realizada, pode-se observar que os alunos que passaram e tiveram um melhor desempenho, no geral tinham mais tempo de interação com o material disponibilizado, além de notas mais elevadas nos exames de Tutor Marked Assessment (TMA) e de Computer Marked Assessment (CMA) em relação aos que não passaram. Isso pode indicar que, focar em iniciativas para aumentar a interatividade do estudante com a plataforma online, aumentando o engajamento com as provas que não são do exame final, é provável que a chance de sucesso dos alunos que não passaram aumentaria. Para que essa análise fique mais assertiva seria necessário seguir com algum modelo de regressão e identificar se esta hipótese é válida.